/*
 * Copyright (c) 2006-2021, Ulandlink Development Team
 * SPDX-License-Identifier: Apache-2.0
 * Change Logs:
 * Date           Author       Notes
 * 2022-02-15     lfz         first version
 */
#include <stdio.h>
#include <string.h>

#include "ftpclient.h"
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>

#define LOG_TAG                    "ftp"
#define LOG_LVL                    LOG_LVL_DBG
#include "ulog.h"

#define RECV_BUFF_MAX              (1024)

#define CMD_PASV                   ("PASV\r\n")

//创建一个socket并返回
static int ftpclient_socket_connect(const char *host, int port)
{
    struct timeval timeout;
    int socket_handle;
    struct hostent *server;
    struct sockaddr_in server_addr;
    
    /* 创建socket */
    socket_handle = socket(AF_INET, SOCK_STREAM, 0);
    if (socket_handle < 0) {
        LOG_E("Create socket failed");
        return -1;
    }

    timeout.tv_sec  = 15;
    timeout.tv_usec = 0;
    /* set receive and send timeout option */
    setsockopt(socket_handle, SOL_SOCKET, SO_RCVTIMEO, (void *) &timeout, sizeof(timeout));
    setsockopt(socket_handle, SOL_SOCKET, SO_SNDTIMEO, (void *) &timeout, sizeof(timeout));

    server = gethostbyname(host);
    if (server == RT_NULL) {
        LOG_E("FTP get host:%s port:%d failed.",host, port);
        closesocket(socket_handle);
        return -1;
    }
    
    server_addr.sin_family = AF_INET;
    server_addr.sin_port   = htons(port);
    server_addr.sin_addr   = *((struct in_addr *)server->h_addr);
    rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero));
    
    LOG_D("Socket:%d Connect Host:%s:%d",socket_handle, inet_ntoa(server_addr.sin_addr), ntohs(server_addr.sin_port));
    
    if (connect(socket_handle, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)) < 0) {
        /* connect failed, close socket */
        LOG_E("FTP connect failed, connect socket(%d) error(%d).", socket_handle, errno);
        closesocket(socket_handle);
        return -1;
    }

    return socket_handle;
}

//连接到一个ftp的服务器，返回socket
static int ftpclient_connect_server(const char *host, int port)
{
    int    ctrl_sock = -1;
    char   *rev_buf = RT_NULL;
    int    result;
    int    len;
    
    ctrl_sock = ftpclient_socket_connect(host, port);
    if (ctrl_sock == -1) {
        goto _exit;
    }
    
    rev_buf = (char*)rt_malloc(RECV_BUFF_MAX);
    if (rev_buf == RT_NULL) {
        ctrl_sock = -1;
        goto _exit;
    }
    rt_memset(rev_buf, 0, RECV_BUFF_MAX);   
    
    len = recv(ctrl_sock, rev_buf, RECV_BUFF_MAX, 0);
    rev_buf[len] = 0;
    
    sscanf(rev_buf, "%d", &result);
    if (result != 220) {
        closesocket(ctrl_sock);
        ctrl_sock = -1;
        goto _exit;
    }
    
_exit:
    if (rev_buf){
        rt_free(rev_buf);
    }
    return ctrl_sock;
}
 
//发送命令,返回结果
static int ftpclient_sendcmd_re(int sock, char *cmd, void *re_buf, int *len)
{
    int result = -RT_ERROR;
    char *buf;
    int  r_len;
    
    if (send(sock, cmd, strlen(cmd), 0) == -1) {
        goto _exit;
    }
        
	buf = (char*)rt_malloc(RECV_BUFF_MAX);
    if (!buf){
        goto _exit;
    }
    rt_memset(buf, 0, RECV_BUFF_MAX);
    
    r_len = recv(sock, buf, RECV_BUFF_MAX, 0);
    if (r_len < 1) {
        goto _exit;
    }
    buf[r_len] = 0;
   
    if (len != NULL) {
        *len = r_len;
    }
        
    if (re_buf != NULL) {
        snprintf(re_buf, 256, "%s", buf);
    }
        
    result = atoi(&buf[0]);
	if (len != RT_NULL){
		*len = atoi(&buf[3]);
	}
    LOG_D("Send CMD:%s Recv ACK:%s result:%d", cmd, buf, atoi(&buf[0]));
    
_exit:
    if (buf){
        rt_free(buf);
    }
    return result;
}
 
//发送命令,返回编号
int ftpclient_sendcmd( int sock, char *cmd, int *fileszie)
{
    char send_buff[256] = {0};
    int  result;
    
    result = ftpclient_sendcmd_re(sock, cmd, send_buff, fileszie);
		LOG_I("result :%d", result);
    if (result == 0){
        sscanf( send_buff, "%d", &result );
    }
    
    return result;
}
 

//登录ftp服务器
static int ftpclient_login_server( int sock, const char *user, const char *pwd)
{
    char buf[256] = {0};
    int  result;
    
    if (!user) {
        return 0;
    }
        
    sprintf(buf, "USER %s\r\n", user );
    result = ftpclient_sendcmd(sock, buf, RT_NULL);
    
    switch(result){
    case 230:
    break;
    
    case 331:
    {
        sprintf(buf, "PASS %s\r\n", pwd);
        if (ftpclient_sendcmd(sock, buf, RT_NULL) != 230 ){
            return -1;
        }
    }
    break;
    
    default:
    return -1;
    }
    
    return 0;    
}
 
//连接到PASV接口
int ftpclient_pasv_connect(int c_sock)
{
    int r_sock = -1;
    int ip_addr[4] = {0};
    int port[2] = {0};  
    char *re_buf = RT_NULL;  
    
    re_buf = rt_malloc(512);
    if (!re_buf) {
        goto _exit;
    } 
    rt_memset(re_buf, 0, 512);
    
    /* 设置PASV被动模式 */
    {
        int len;
        int send_re;
        /* 发送PASV 命令 */
        send_re = ftpclient_sendcmd_re(c_sock, CMD_PASV, re_buf, &len);  
        if (send_re < 0){
            goto _exit;
        } else {
            /* 227 Entering Passive Mode (192,168,0,170,32,174) */
            sscanf(re_buf, "%*[^(](%d,%d,%d,%d,%d,%d)", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3], &port[0], &port[1]);
        }
    }
 
    /* 连接PASV端口 */
    {
        char buf[32] = {0};
        rt_memset(buf, 0, 32);
        sprintf(buf, "%d.%d.%d.%d",ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3]);
        r_sock = ftpclient_socket_connect(buf, port[0] * 256 + port[1]);
    }

_exit:
    if (re_buf){
        rt_free(re_buf);
    }  
    return r_sock;
}
 
//表示类型
int ftpclient_type( int c_sock, char mode)
{
    char buf[16] = {0};
    sprintf(buf, "TYPE %c\r\n", mode);
    
    if (ftpclient_sendcmd(c_sock, buf, RT_NULL) != 200 ) {
        return -1;
    }
    return 0;
}
 
//改变工作目录
int ftpclient_cwd( int c_sock, char *path)
{
    char buf[32] = {0};
    int  re;
    sprintf( buf, "CWD %s\r\n", path);
    
    re = ftpclient_sendcmd(c_sock, buf, RT_NULL);
    if (re != 250 ){
        return -1;
    }
    return 0;
}

//发送PWD命令
int ftpclient_pwd( int c_sock)
{
    int  re; 
    re = ftpclient_sendcmd(c_sock, "PWD\r\n", RT_NULL);
    if (re != 257 ){
        return -1;
    }
    return 0;
}

//获取文件长度
int ftpclient_cmd_size(int c_sock, char *path, int *fileszie)
{
  int resp_status;
    int ret = -1;
    char* buf = RT_NULL;
    
    buf = (char*)rt_malloc(512);
    if (!buf) {
        goto _exit;
    } 
    rt_memset(buf, 0, 512);
    
    //发送RETR命令
    sprintf(buf, "SIZE %s\r\n", path);
    resp_status = ftpclient_sendcmd(c_sock, buf, fileszie);
    if (resp_status != 213) {
        goto _exit;
    } else {
        ret = 0;
    }
     
_exit:
    if (buf){
        rt_free(buf);
    }
    return ret;
}

//发送RETR命令
int ftpclient_cmd_retr(int c_sock, char *path)
{
    int resp_status;
    int ret = -1;
    char* buf = RT_NULL;
    
    buf = (char*)rt_malloc(512);
    if (!buf) {
        goto _exit;
    } 
    rt_memset(buf, 0, 512);
    
    //发送RETR命令
    sprintf(buf, "RETR %s\r\n", path);
    resp_status = ftpclient_sendcmd(c_sock, buf, RT_NULL);
    if (resp_status >= 300 || resp_status == 0) {
        goto _exit;
    } else {
        ret = 0;
    }
     
_exit:
    if (buf){
        rt_free(buf);
    }
    return ret;
}

//链接服务器
int ftpclient_connect(const char *host, int port, const char *user, const char *pwd)
{
    int c_sock;
    
    c_sock = ftpclient_connect_server(host, port);
    if (c_sock == -1) {
        LOG_E("Connect server Failed.");
        return -1;
    }
        
    if (ftpclient_login_server(c_sock, user, pwd ) == -1 ) {
        closesocket( c_sock );
        return -1;
    }
    return c_sock;
}

//关闭socket
int ftpclient_close_socket(int socket)
{
    int re = 0;
    if (socket > 0){
        closesocket(socket);
    }
    return re;
}

//断开服务器
int ftpclient_quit(int c_sock)
{
    int re = 0;
    re = ftpclient_sendcmd(c_sock, "QUIT\r\n", RT_NULL);
    closesocket(c_sock);
    return re;
}

/**
 * @brief 从url获取ftpurlinfo信息
 * 
 * url格式：ftp://user:password@hostname:port/path/filename
 
 * @param url 
 * @param res 
 * @return int 
 */
int ftpclient_get_urlinfo(const char *url, struct ftpclient_urlinfo *res)
{
    char *p;
    
    int len = strlen(url) + 2;
    res->user = res->pw = res->host = res->path = NULL;
    
    res->buffer = rt_malloc(len);
    if (!res->buffer) {
        return -1;
    }
        
    strcpy(res->buffer, url);
    res->buffer[len-1] = 0;
    res->port = 21;

    if (strncmp(res->buffer,"ftp://",6) != 0) {
        return -1;
    }
    /*获取host*/
    res->host = strstr(res->buffer,"@");
    if (res->host == NULL) {
        /*没有指定user和password*/
        res->host = res->buffer + 6;
    } else  {
        /*获取user*/
        res->user = res->buffer + 6;
        res->host[0] = 0;
        res->host ++;
        p = strstr(res->user,":");
        if (!p) {
            return -2;
        }
            
        p[0] = 0;
        p++;
        /*获取pw*/
        res->pw = p; 
    }
    /*获取path*/
    p = strstr(res->host,"/");
    if (!p) {
        return -3;
    }
    *p = 0;
    p = &res->buffer[len - 3];
    for (; *p != 0; p--) {
        *(p+1) = *p;
    }
    p++;
    *p = '/';
    res->path = p;
    /*获取端口号*/
    p = strstr(res->host, ":");
    /* 如果指定了端口号，从url获取ftp端口*/
    if (p) {
        p[0] = 0;
        p ++;
        res->port = atoi(p);
    }
    return 0;    
}

/**
 * @brief 释放ftpurlinfo资源
 * 
 * @param res 
 */
void ftpclient_free_urlinfo(struct ftpclient_urlinfo *res)
{
    if (res->buffer) {
        rt_free(res->buffer);
        res->buffer = NULL;
    }
}

//接收数据
int ftpclient_read(int socket, void *buffer, size_t length)
{
    rt_memset(buffer, 0, length);
    return recv(socket, buffer, length, 0);
}

//发送数据
int ftpclient_send(int socket, const void *buffer, size_t length)
{
    return send(socket, buffer, length, 0);
}
